﻿using System;
using Audit.Core;
using Audit.WebApi;
using K9Nano.AspNetCore.Audit;
using K9Nano.AspNetCore.Authorization;
using K9Nano.AspNetCore.Filters;
using K9Nano.AspNetCore.MultiTenancy;
using K9Nano.AspNetCore.MultiTenancy.Resolvers;
using K9Nano.AspNetCore.Runtime;
using K9Nano.Configuration.Startup;
using K9Nano.Dependency;
using K9Nano.MultiTenancy;
using K9Nano.Runtime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class AspNetCoreStartupExtensions
    {
        public static IK9NanoAppBuilder AddK9NanoAspNetCore(this IK9NanoAppBuilder builder)
        {
            var services = builder.Services;

            var thisAssemble = typeof(AspNetCoreStartupExtensions).Assembly;

            services.RegisterConventionalAssembly(thisAssemble);

            builder.MvcApplicationPartAeAssemblies.Add(thisAssemble);

            if (StartupConfiguration.Global.MultiTenancyConfig.IsEnabled)
            {
                StartupConfiguration.Global.MultiTenancyConfig.Resolvers.Add<DomainTenantResolveContributor>();
                StartupConfiguration.Global.MultiTenancyConfig.Resolvers.Add<HttpHeaderTenantResolveContributor>();
                StartupConfiguration.Global.MultiTenancyConfig.Resolvers.Add<HttpCookieTenantResolveContributor>();
                services.Replace(ServiceDescriptor.Transient<ITenantResolverCache, HttpContextTenantResolverCache>());
            }

            services.AddHttpContextAccessor();

            services.Replace(ServiceDescriptor.Scoped<IClaimAccessor, HttpClaimAccessor>());

            services.Replace(ServiceDescriptor.Singleton<IAuthorizationPolicyProvider, K9AuthorizationPolicyProvider>());
            services.AddScoped<IAuthorizationHandler, K9RbacRequirementHandler>();

            /* Filters */
            services.Configure<MvcOptions>(options =>
            {
                options.Filters.Add<K9ExceptionFilter>();
                options.Filters.Add<K9ActionFilter>();
            });

            return builder;
        }

        public static IK9NanoAppBuilder AddDefaultAudit(this IK9NanoAppBuilder builder)
        {
            return builder.AddDefaultAudit(ServiceDescriptor.Scoped<AuditDataProvider, EfCoreWebApiAuditDataProvider>());
        }

        public static IK9NanoAppBuilder AddDefaultAudit(this IK9NanoAppBuilder builder,
            ServiceDescriptor dataProviderServiceDescriptor)
        {
            builder.Services.Configure<MvcOptions>(options =>
            {
                options.AddAuditFilter(config => config
                    .LogAllActions()
                    .WithEventType("{controller}.{action}")
                    .IncludeHeaders(false)
                    .IncludeRequestBody(false)
                    .IncludeModelState()
                    .IncludeResponseBody(ctx => ctx.HttpContext.Response.StatusCode == 200));
            });
            builder.Services.Add(dataProviderServiceDescriptor);

            return builder;
        }

        public static IMvcBuilder AddApplicationPart(this IMvcBuilder mvcBuilder, IK9NanoAppBuilder builder)
        {
            foreach (var assembly in builder.MvcApplicationPartAeAssemblies)
            {
                mvcBuilder.AddApplicationPart(assembly);
            }

            return mvcBuilder;
        }

        public static IApplicationBuilder UseDefaultAudit(this IApplicationBuilder app)
        {
            app.Use(async (context, next) =>
            {
                context.Request.EnableBuffering(); // read request body multiple times
                await next();
            });

            //app.UseAuditMiddleware(_ => _
            //    .FilterByRequest(rq => !rq.Path.Value.EndsWith("favicon.ico") && rq.h)
            //    .WithEventType("{url}")
            //    .IncludeHeaders(false)
            //    .IncludeResponseHeaders(false)
            //    .IncludeRequestBody()
            //    .IncludeResponseBody(ctx => ctx.Response.StatusCode == 200));

            Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
            {
                var httpAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
                scope.Event.CustomFields["CorrelationId"] = httpAccessor.HttpContext?.TraceIdentifier;
            });

            var auditDataProvider = app.ApplicationServices.GetRequiredService<AuditDataProvider>();

            Audit.Core.Configuration.Setup()
                .UseCustomProvider(auditDataProvider);

            Audit.Core.Configuration.JsonSettings = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
            };

            return app;
        }
    }
}